﻿using System;

namespace Aeon.Emulator.Sound.FM
{
    /// <summary>
    /// Calculates OPL3 vibrato multipliers.
    /// </summary>
    internal sealed class VibratoGenerator
    {
        private const double DVB1 = 1.0040515439559157;
        private const double DVB2 = 1.0081195029202583;

        private static readonly double[,] VibratoValues =
        {
            { 1, Math.Sqrt(DVB1), DVB1, Math.Sqrt(DVB1), 1, 1.0 / Math.Sqrt(DVB1), 1.0 / DVB1, 1.0 / Math.Sqrt(DVB1) },
            { 1, Math.Sqrt(DVB2), DVB2, Math.Sqrt(DVB2), 1, 1.0 / Math.Sqrt(DVB2), 1.0 / DVB2, 1.0 / Math.Sqrt(DVB2) }
        };

        private int dvb;
        private int currentIndex;

        /// <summary>
        /// Initializes a new instance of the <see cref="VibratoGenerator"/> class.
        /// </summary>
        public VibratoGenerator()
        {
            this.CurrentValue = VibratoValues[0, 0];
        }

        /// <summary>
        /// Gets or sets the DVB value.
        /// </summary>
        public int DVB
        {
            get { return this.dvb; }
            set
            {
                if(value < 0 || value > 1)
                    throw new ArgumentOutOfRangeException();

                this.dvb = value;
                this.CurrentValue = VibratoValues[this.dvb, this.currentIndex >> 10];
            }
        }
        /// <summary>
        /// Gets the current vibrato multiplier.
        /// </summary>
        public double CurrentValue { get; private set; }

        /// <summary>
        /// Increments the vibrato index.
        /// </summary>
        public void Increment()
        {
            this.currentIndex = (this.currentIndex + 1) & 0x1FFF;
            this.CurrentValue = VibratoValues[this.dvb, this.currentIndex >> 10];
        }
    }
}
